

; Vector Functions
; provides user-friendly interface for doing operations on vectors (including some geometric operations)
; first general functions that work on vectors of any size (using standard operators)
; then optimized versions (defined in the global namespace and therefore overrites the previous ones) that specialize on 2 element vectors and use floating-point arithmetic


(load "optimization.scm")
(require srfi/43) ; for vector-copy; vector-copy!

; get-
(define (get-x vector)
  (vector-ref vector 0))
(define (get-y vector)
  (vector-ref vector 1))

; set-
(define (set-x vector value)
  (vector-set! vector 0 value))
(define (set-y vector value)
  (vector-set! vector 1 value))


; (vector-op + #(1 2) #(3 4)) -> #(4 6)
(define (vector-op op v1 v2)
  (let* ([len (vector-length v1)]
         [res (make-vector len)])
    (let vector-op-loop ([pos 0])
      (when (< pos len)
        (vector-set! res pos (op (vector-ref v1 pos) (vector-ref v2 pos)))
        (vector-op-loop (add1 pos))))
    res))

; (vector-element-op + #(1 2)) -> 3
(define (vector-element-op op v)
  (let* ([len (vector-length v)])
    (let vector-element-op-loop ([pos 1]
                                 [res (vector-ref v 0)])
      (if (= pos len)
          res
          (vector-element-op-loop (add1 pos) (op res (vector-ref v pos)))))))

; (vector-scalar-op + #(1 2) 2) -> #(3 4)
(define (vector-scalar-op op v scalar)
  (let* ([len (vector-length v)]
         [res (make-vector len)])
    (let vector-scalar-op-loop ([pos 0])
      (when (< pos len)
        (vector-set! res pos (op scalar (vector-ref v pos)))
        (vector-scalar-op-loop (add1 pos))))
    res))

; Dot (scalar) product
(define (vector-dot-product v1 v2)
  (vector-element-op + (vector-op * v1 v2)))

; Vector "length"
(define (vector-abs v)
  (inexact->exact (round (sqrt (vector-dot-product v v)))))

; Project v1 onto v2
(define (vector-project v1 v2)
  (vector-scalar-op * v2 (/ (vector-dot-product v1 v2) (vector-dot-product v2 v2))))

; Mirror v in the plane described by mirror-normal
(define (vector-mirror v mirror-normal)
  (let* ([v-parallel  (vector-project v mirror-normal)]
         [v-ortogonal (vector-op - v v-parallel)])
    (vector-op + v-ortogonal (vector-scalar-op * v-parallel -1))))



;
; 2D Float Specializations of above
;
(define (vector-op op v1 v2)
  (vector (op (vector-ref v1 0) (vector-ref v2 0)) (op (vector-ref v1 1) (vector-ref v2 1))))
(define (vector-element-op op v)
  (op (vector-ref v 0) (vector-ref v 1)))
(define (vector-scalar-op op v scalar)
  (vector (op (vector-ref v 0) scalar) (op (vector-ref v 1) scalar)))
(define (vector-dot-product v1 v2)
  (fl+ (fl* (vector-ref v1 0) (vector-ref v2 0)) (fl* (vector-ref v1 1) (vector-ref v2 1))))
(define (vector-abs v)
  (flsqrt (fl+ (fl* (vector-ref v 0) (vector-ref v 0)) (fl* (vector-ref v 1) (vector-ref v 1)))))
(define (vector-project v1 v2)
  (vector-scalar-op fl* v2 (fl/ (vector-dot-product v1 v2) (vector-dot-product v2 v2))))
(define (vector-mirror v mirror-normal)
  (let* ([v-parallel  (vector-project v mirror-normal)]
         [v-ortogonal (vector-op - v v-parallel)])
    (vector-op fl+ v-ortogonal (vector-scalar-op fl* v-parallel #i-1))))




